49  创建序列和数据框

本章定位(复习与补充训练层):本章对应正课第10章《Pandas Series和DataFrame创建》(章节 10),用于复习、补缺与额外练习。建议先不看讲解,直接尝试下方平台任务,再对照解析补弱项。本章不属于必修主线。先做本章『动手与思考』第 1 题与平台任务自测,通过即可跳过本章。

49.1 引言 Pandas数据结构在金融分析中的核心地位

Pandas 是 Python 数据分析的核心库,建立在 NumPy 之上,提供了 Series 与 DataFrame 两种数据结构。本章围绕”创建 Series—创建 DataFrame—设置索引—结构互转”复习这些能力,并完成平台四个任务。

49.2 本章学习目标

通过本章的复习与补充训练,你将能够:

  1. pd.Series(data, index) 创建一维序列,并说明列表、字典、NumPy 数组三种数据源下索引分别来自哪里
  2. pd.DataFrame(data, index=..., columns=...) 创建二维数据框,分清行索引与列名各自的作用
  3. 在 Series、DataFrame、NumPy 数组、Python 列表之间互相转换(list()np.array().values)
  4. pd.date_range()set_index() 为数据框设置有意义的日期行标签,并用 type() 确认对象类型,独立完成平台四个任务

49.3 创建Series对象

Series是Pandas的一维数据结构,可以理解为”带标签的数组”。在金融应用中,Series非常适合存储单只股票的价格序列、单只股票的交易量序列、或某一天的多个股票价格等一维数据。

既然 NumPy 数组已经能装下这些数值,为什么还需要 Series?因为 NumPy 数组在数据分析中存在三个关键限制。

理论背景:从NumPy到Pandas的演进

NumPy数组提供了高效的数值计算能力,但在数据分析中存在三个关键限制:

  1. 缺乏标签:NumPy数组只能通过整数位置访问,无法使用有意义的标签(如股票代码、日期)
  2. 数据类型单一:NumPy数组的所有元素必须是相同类型
  3. 缺失值处理:NumPy使用NaN表示缺失值,但处理不够灵活

Pandas通过Series和DataFrame这两种数据结构完美解决了这些问题,使其成为金融数据分析的理想工具。

平台任务(平台原始代码)

以下代码与教学平台任务要求完全一致:

任务要求:任务一,用国泰金鑫 8 个交易日的净值数组与交易日数组创建带日期索引的 Series,并把某日 5 只基金净值列表转为以基金简称为索引的 Series,分别查看两者类型;任务二,用 3 只基金 3 个交易日的净值数组创建数据框,行索引取日期切片、列名取基金名切片,并查看其类型;任务三,用 8 个交易日 × 5 只基金的净值数组创建完整净值数据框并打印;任务四,把 Series 转为列表、把 Series 与 DataFrame 分别转为 NumPy 数组,并打印数据框转出的数组。请将代码原样输入教学平台(注释除外),判定以平台为准。

列表 49.1: 平台原始代码 (1)-(4)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import numpy as np
import pandas as pd  # 导入Pandas数据分析库

value_guotai = np.array([1.5284,1.4596,1.3745,1.4034,1.3935,1.39,1.412,1.4164]) #国泰金鑫股票基金2024年8月1日起的8个交易日(至8月12日)净值数据的数组
date = np.array(["2024-08-01","2024-08-02","2024-08-05","2024-08-06","2024-08-07","2024-08-08","2024-08-09","2024-08-12"]) #交易日数组

series_guotai = pd.Series(data=value_guotai,index=date) #创建序列
print(type(series_guotai))  #查看变量的数据结构

data=[1.4596,1.001,0.897,2.051,1.7385];index=["国泰金鑫","中海医疗","华夏优势","富国城镇","上投摩根"] #创建8月2日基金净值的数组

value_0403 = pd.Series(data,index)  # 创建Series序列value_0403
print(type(value_0403))  # 输出数据类型


#任务二
import numpy as np
import pandas as pd  # 导入Pandas数据分析库

date = np.array(["2024-08-01","2024-08-02","2024-08-05","2024-08-06","2024-08-07","2024-08-08","2024-08-09","2024-08-12"]) #交易日数组

name = np.array(["国泰金鑫","中海医疗","华夏优势","富国城镇","上投摩根"])  #基金名称的数组并且用基金简称

data_3fund_array = np.array([[1.01,0.8918,2.038],[0.995,0.8833,2.025],[1.002,0.8735,2.032]]) #3只基金在2024年8月8日至12日净值的数组

data_3fund = pd.DataFrame(data=data_3fund_array,index=date[5:],columns=name[1:4]) #转成一个数据框

print(type(data_3fund))  # 输出数据类型

#任务三
import numpy as np
import pandas as pd  # 导入Pandas数据分析库

date = np.array(["2024-08-01","2024-08-02","2024-08-05","2024-08-06","2024-08-07","2024-08-08","2024-08-09","2024-08-12"]) #交易日数组

name = np.array(["国泰金鑫","中海医疗","华夏优势","富国城镇","上投摩根"])  # 创建NumPy数组name

# 创建NumPy数组value_total
value_total = np.array([[1.5284,0.991,0.9122,2.069,1.7742],[1.4596,1.001,0.897,2.051,1.7385],[1.3745,1,0.8786,2.023,1.6843],[1.4034,1.013,0.8944,2.027,1.6917],

[1.3935,1.007,0.8944,2.031,1.6957],[1.39,1.01,0.8918,2.038,1.6955],[1.412,0.995,0.8833,2.025,1.6938],[1.6938,1.002,0.8735,2.032,1.6899]]) #创建数组

data_total = pd.DataFrame(data=value_total,index=date,columns=name)  #转为数据框

print(data_total)  # 输出数据数据

#任务四
import numpy as np

import pandas as pd  # 导入Pandas数据分析库

value_guotai = np.array([1.5284,1.4596,1.3745,1.4034,1.3935,1.39,1.412,1.4164]) #国泰金鑫股票基金2024年8月1日起的8个交易日(至8月12日)净值数据的数组

date = np.array(["2024-08-01","2024-08-02","2024-08-05","2024-08-06","2024-08-07","2024-08-08","2024-08-09","2024-08-12"]) #交易日数组

series_guotai = pd.Series(data=value_guotai,index=date)  # 创建Series序列series_guotai

data=[1.4596,1.001,0.897,2.051,1.7385];index=["国泰金鑫","中海医疗","华夏优势","富国城镇","上投摩根"] #创建8月2日基金净值的数组

value_0403 = pd.Series(data,index)  # 创建Series序列value_0403

name = np.array(["国泰金鑫","中海医疗","华夏优势","富国城镇","上投摩根"])  #基金名称的数组并且用基金简称

data_3fund_array =  np.array([[1.01,0.8918,2.038],[0.995,0.8833,2.025],[1.002,0.8735,2.032]])#3只基金在2024年8月8日至12日净值的数组

data_3fund = pd.DataFrame(data=data_3fund_array,index=date[5:],columns=name[1:4])  # 创建数据框data_3fund


list_guotai = list(series_guotai)#国泰金鑫8与1日到12日净值数据的序列转为列表

array_0403 = np.array(value_0403) #8月2日5只基金净值的序列转为数组

array_3fund = data_3fund.values  #8月8日到12日3只基金净值的数据框变为数组
print(array_3fund)  # 输出基金数据

题面笔误提示:任务三 value_total 末行国泰金鑫 2024-08-12 取值 1.6938,与任务一/任务四同日的 1.4164 不一致(疑为题面串行笔误)。请按平台原始题面原样输入,以平台判定为准。

预期输出(本机 Python 实际运行结果,四个任务在同一代码块中顺序执行;具体以平台运行结果为准):

  • 任务一:两行类型输出
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
  • 任务二:一行类型输出 <class 'pandas.core.frame.DataFrame'>
  • 任务三:打印 8 行 × 5 列的净值数据框,行索引为 2024-08-01 至 2024-08-12 的 8 个交易日,列名依次为国泰金鑫、中海医疗、华夏优势、富国城镇、上投摩根;首行 1.5284 0.991 0.9122 2.069 1.7742,末行(含题面笔误值)1.6938 1.002 0.8735 2.032 1.6899
  • 任务四:打印任务二数据框转出的 3×3 NumPy 数组:
[[1.01   0.8918 2.038 ]
 [0.995  0.8833 2.025 ]
 [1.002  0.8735 2.032 ]]
列表 49.2: 创建Pandas Series的三种方法
# =============================================================================
# 题目:创建Pandas Series对象
# =============================================================================
# 本任务演示三种不同的Series创建方法,适用于不同的数据来源场景

# ==================== 导入必要的库 ====================
# pandas:Python数据分析的核心库
import pandas as pd
# numpy:Python科学计算的基础库,pandas依赖numpy
import numpy as np

# ==================== 方法1:从Python列表创建Series ====================
# 场景:当你有一系列数值数据需要存储为Series时
# 列表[10, 20, 30, 40, 50]包含5个整数元素
# pd.Series()函数将列表转换为Series对象
# Series会自动为这些元素创建默认的整数索引(0, 1, 2, 3, 4)
s1 = pd.Series([10, 20, 30, 40, 50])
# 结果:一个Series对象,值为[10, 20, 30, 40, 50],索引为[0, 1, 2, 3, 4]

# ==================== 方法2:从字典创建Series ====================
# 场景:当你有键值对数据,且键本身就是有意义的标签时
# 字典的键(股票名称)会成为Series的索引
# 字典的值(股价)会成为Series的值
# name参数指定Series的名称,用于标识这个Series
s2 = pd.Series({
    '贵州茅台': 1850,  # 键'贵州茅台',值1850(股价)
    '五粮液': 220,     # 键'五粮液',值220(股价)
    '招商银行': 45      # 键'招商银行',值45(股价)
}, name='股价')  # 设置Series名称为'股价'
# 结果:一个Series对象,索引为['贵州茅台', '五粮液', '招商银行']
#       值为[1850, 220, 45],名称为'股价'

# ==================== 方法3:从NumPy数组创建Series ====================
# 场景:当你有数值计算的结果需要转换为Series时
# np.random.randn(5)生成5个标准正态分布的随机数(均值0,标准差1)
# 这些随机数可以用于模拟股票收益率等金融数据
np.random.seed(42)  # 固定随机种子,保证每次运行结果一致
s3 = pd.Series(np.random.randn(5))
# 结果:一个Series对象,包含5个随机数,索引为默认的[0, 1, 2, 3, 4]

# ==================== 打印输出Series对象 ====================
print('Series 1 (从列表创建):')
print(s1)  # 输出Series的完整表示:索引、值、数据类型

print('\nSeries 2 (从字典创建):')
print(s2)  # 输出带名称的Series
print(f'索引: {s2.index.tolist()}')  # .index获取索引对象,.tolist()转换为Python列表
# 输出:['贵州茅台', '五粮液', '招商银行']

代码深度解析:

  1. Series的组成结构:
    • 索引(index):每个数据点的标签,用于定位和访问数据
    • 值(values):实际存储的数据
    • 名称(name):Series对象的标识(可选)
    • 数据类型(dtype):值的类型(如int64、float64、object等)
  2. 从字典创建的优势:
    • 索引自动设置为字典的键,无需手动指定
    • 适合表示有标签的数据(如股票名称到价格的映射)
    • 比从列表创建后设置索引更简洁
  3. Series与NumPy数组的关系:
    • Series基于NumPy数组构建
    • Series.values返回底层的NumPy数组
    • Series比NumPy数组多了索引功能

49.4 创建DataFrame

DataFrame是Pandas的二维数据结构,类似于Excel表格或SQL数据库的表。在金融分析中,DataFrame是最常用的数据结构,用于存储和分析多只股票的多指标数据。把 Series 与 DataFrame 放在一起对照,两种结构的分工一目了然:

Pandas两种核心数据结构:

特性 Series DataFrame
维度 一维 二维
类比 带标签的数组 带标签的表格/Excel工作表
索引 必须有索引 必须有行索引,列名是另一种索引
金融应用 单只股票的价格序列 多只股票的多指标数据表
列表 49.3: 创建Pandas DataFrame
# =============================================================================
# 题目:创建Pandas DataFrame对象
# =============================================================================
# 本任务演示如何从字典创建DataFrame来存储股票数据

# ==================== 从字典创建DataFrame ====================
# 场景:当你的数据是以字典形式组织时,每个键是一个列名,每个值是列数据
# 字典的键('股票代码', '股票名称', '股价', '涨跌幅')将成为DataFrame的列名
# 字典的值都是列表,这些列表的长度必须相同(这里都是3个元素)
data = {
    '股票代码': ['600519.SH', '000858.SZ', '600036.SH'],  # 第一列:股票代码
    '股票名称': ['贵州茅台', '五粮液', '招商银行'],     # 第二列:公司名称
    '股价': [1850.0, 220.5, 45.2],                   # 第三列:股价(浮点数)
    '涨跌幅': [0.05, -0.02, 0.03]                     # 第四列:涨跌幅(浮点数)
}

# pd.DataFrame()函数将字典转换为DataFrame对象
# Pandas会自动:
#   1. 使用字典的键作为列名
#   2. 对齐所有列表的数据(创建3行×4列的表格)
#   3. 自动生成默认的行索引(0, 1, 2)
df = pd.DataFrame(data)

# ==================== 打印DataFrame的基本信息 ====================
print('DataFrame内容:')
print(df)
# 输出格式:
#    股票代码   股票名称     股价  涨跌幅
# 0  600519.SH   贵州茅台  1850.0  0.05
# 1  000858.SZ   五粮液    220.5  -0.02
# 2  600036.SH   招商银行   45.2  0.03
# 左侧的0,1,2是行索引,顶部是列名

# ==================== 查看DataFrame的属性 ====================
print(f'\nDataFrame形状: {df.shape}')  # shape属性返回(行数, 列数)
# 输出:(3, 4) - 表示3行4列的表格
print(f'列名列表: {df.columns.tolist()}')  # columns获取所有列名,tolist()转换为列表
# 输出:['股票代码', '股票名称', '股价', '涨跌幅']

49.5 从NumPy数组创建DataFrame

在科学计算和金融建模中,数据往往以NumPy数组的形式产生(如数值模拟的结果、矩阵运算的结果)。将NumPy数组转换为DataFrame可以添加有意义的标签,使数据更易读和易分析。

列表 49.4: 从NumPy数组创建DataFrame
# =============================================================================
# 题目:从NumPy数组创建DataFrame
# =============================================================================
# 本任务演示如何将NumPy数值数组转换为带标签的DataFrame

# ==================== 创建随机NumPy数组 ====================
# np.random.randn(5, 3)生成一个5行×3列的数组
# 5行:5个观测样本(如5个交易日或5只股票)
# 3列:每个样本有3个特征(如开盘价、最高价、最低价)
# 这些数据服从标准正态分布(均值0,标准差1)
np.random.seed(42)  # 固定随机种子,保证每次运行结果一致
arr = np.random.randn(5, 3)
# 结果:一个5×3的NumPy数组,包含随机数值

# ==================== 将NumPy数组转换为DataFrame ====================
# pd.DataFrame()函数可以将NumPy数组转换为DataFrame
# arr参数:要转换的NumPy数组(数据来源)
# columns参数:指定DataFrame的列名(列表形式)
# index参数:指定DataFrame的行标签(列表形式)
df_random = pd.DataFrame(
    arr,                           # 数据来源:5×3的随机数组
    columns=['特征1', '特征2', '特征3'],  # 列名:为3列分别命名
    index=['样本1', '样本2', '样本3', '样本4', '样本5']  # 行标签:为5行分别命名
)
# 结果:一个5×3的DataFrame,行标签为"样本1"到"样本5",列名为"特征1"到"特征3"

# ==================== 打印DataFrame ====================
print('从NumPy数组创建的DataFrame:')
print(df_random)
# 输出格式:
#             特征1      特征2      特征3
# 样本1  随机值1   随机值2   随机值3
# 样本2  随机值1   随机值2   随机值3
# ...(共5行)

49.6 从列表的列表创建DataFrame

当数据以嵌套列表的形式组织时(例如从CSV文件读取的数据,或从API获取的JSON数据),也可以直接创建DataFrame。

列表 49.5: 从列表的列表创建DataFrame
# =============================================================================
# 题目:从列表的列表创建DataFrame
# =============================================================================
# 本任务演示如何将嵌套列表转换为DataFrame

# ==================== 准备嵌套列表数据 ====================
# 场景:当你有多个记录,每个记录包含多个字段时
# 外层列表包含3个元素(3条股票记录)
# 每个元素是一个列表,包含[名称, 价格, 涨跌幅]三个字段
data_list = [
    ['贵州茅台', 1850.0, 0.05],    # 第1条记录
    ['五粮液', 220.5, -0.02],      # 第2条记录
    ['招商银行', 45.2, 0.03]      # 第3条记录
]
# data_list是一个"列表的列表"结构

# ==================== 从列表的列表创建DataFrame ====================
# pd.DataFrame()函数可以接受嵌套列表作为输入
# data_list参数:数据源(嵌套列表)
# columns参数:指定列名,为每列数据赋予有意义的名称
#   - '名称':第一列数据(股票名称)
#   - '价格':第二列数据(股价)
#   - '涨跌幅':第三列数据(涨跌百分比)
df_from_list = pd.DataFrame(
    data_list,                    # 数据:嵌套列表
    columns=['名称', '价格', '涨跌幅']  # 列名:指定3个列的名称
)
# Pandas会自动将嵌套列表的每个子列表对应到一行
# 子列表的第0个元素对应第1列,第1个元素对应第2列,以此类推

# ==================== 打印DataFrame ====================
print('从列表的列表创建的DataFrame:')
print(df_from_list)
# 输出格式:
#      名称     价格  涨跌幅
# 0  贵州茅台  1850.0  0.05
# 1  五粮液    220.5  -0.02
# 2  招商银行   45.2  0.03

49.7 设置DataFrame的索引

索引(Index)是Pandas数据结构的重要特性,它为每行数据提供了一个有意义的标识。在金融时间序列分析中,通常将日期设置为索引,以便进行时间序列操作(如按日期切片、重采样等)。

列表 49.6: 设置DataFrame的行索引
# =============================================================================
# 题目:设置DataFrame的行索引
# =============================================================================
# 本任务演示如何为DataFrame设置有意义的行标签

# ==================== 创建示例DataFrame ====================
# 创建一个简单的DataFrame用于演示
df = pd.DataFrame({
    '价格': [10, 20, 30, 40, 50],        # 第1列:价格数据
    '销量': [100, 200, 150, 300, 250]    # 第2列:销量数据
})
# 默认会创建整数索引0, 1, 2, 3, 4

# ==================== 生成日期序列作为索引 ====================
# pd.date_range()函数生成日期序列(DatetimeIndex对象)
# '2024-01-01':起始日期
# periods=5:生成5个日期
# 默认频率是'D'(每天),也可以指定'B'(工作日)、'M'(月)等
dates = pd.date_range('2024-01-01', periods=5)
# 结果:DatetimeIndex(['2024-01-01', '2024-01-02', ..., '2024-01-05'])

# ==================== 将日期设置为DataFrame的索引 ====================
# set_index()方法将某列(或外部数据)设置为行索引
# dates参数:使用前面生成的日期序列作为新索引
# 原来的整数索引(0, 1, 2, 3, 4)会被替换
df_indexed = df.set_index(dates)
# 结果:DataFrame的行索引变成了日期,而不是数字

# ==================== 打印带日期索引的DataFrame ====================
print('带日期索引的DataFrame:')
print(df_indexed)
# 输出格式:
#             价格  销量
# 2024-01-01   10  100
# 2024-01-02   20  200
# 2024-01-03   30  150
# ...(共5行)
# 左侧是日期索引,不再是0, 1, 2...

# ==================== 索引的好处 ====================
# 现在可以使用日期来选择数据,例如:
# df_indexed.loc['2024-01-01']  # 获取特定日期的数据
# df_indexed.loc['2024-01-01':'2024-01-03']  # 获取日期范围的数据
# 这比使用整数位置(如df.iloc[0:3])更直观、更不容易出错

49.8 本章小结

要点:

  • Series 是带标签的一维数组,由 index(标签)与 values(数据)构成,可选 name 标识;DataFrame 是带行索引与列名的二维表
  • 数据源决定写法:列表或 NumPy 数组作数据时需另外给出 index/columns;字典的键自动成为 Series 的索引或 DataFrame 的列名
  • 双向转换:.values 把 Series/DataFrame 还原为 NumPy 数组,list() 把 Series 转为列表,np.array()pd.Series()pd.DataFrame() 在四类结构之间自由搬运
  • set_index() 用日期等有意义的标签替换默认整数索引,为后续按标签切片、时间序列操作打基础;type() 是确认对象类型的第一手段

易错点:

  • index 长度必须与数据长度一致、columns 数量必须与列数一致,否则构造直接报错
  • 用字典构造 DataFrame 时各值列表的长度必须相同,长短不一会抛错
  • 平台任务二用 date[5:]name[1:4] 切片截取部分索引,切片结果与数据行列数对不上是高频报错来源(3 行数据配 3 个日期、3 个基金名)
  • pd.Series(data, index) 的两个位置参数顺序容易写反,改用关键字参数 data=index= 更稳妥
  • Series 打印时会同时输出索引、数值与 dtype 三部分,不要把 dtype 行误认为数据的一部分

49.9 动手与思考

以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。

  1. 输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。

    import pandas as pd
    
    s = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
    print(s['b'])
    print(s.values)
    
    df = pd.DataFrame({'价格': [1.0, 2.0], '数量': [3, 4]}, index=['甲', '乙'])
    print(df.shape)
    print(df.columns.tolist())

    参考答案(先写下你的预测再点开)

    解题思路s['b'] 按标签取出标量 20s.values 剥掉索引,返回 NumPy 数组 [10 20 30](注意打印形式没有逗号)。字典构造 DataFrame 时键成为列名,故 df.shape(2, 2)(2 行 2 列),df.columns.tolist()['价格', '数量']

    # 验证脚本:Series取值与DataFrame结构
    import pandas as pd  # 导入pandas库
    s = pd.Series([10, 20, 30], index=['a', 'b', 'c'])  # 显式指定标签索引的序列
    print(s['b'])  # 按标签取单个值,输出标量20
    print(s.values)  # 只取数据部分,返回NumPy数组
    df = pd.DataFrame({'价格': [1.0, 2.0], '数量': [3, 4]}, index=['甲', '乙'])  # 字典键作列名
    print(df.shape)  # 输出(行数,列数)
    print(df.columns.tolist())  # 列名转为列表输出

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    20
    [10 20 30]
    (2, 2)
    ['价格', '数量']

    回扣主线:Series 的索引/数据两要素与 .values 的双向转换见第 章节 10 章。

  2. 自测回忆:不看正文,分别说明从列表和从字典创建 Series 时,索引分别由谁提供;再写出把 Series 转为列表、把 DataFrame 转为 NumPy 数组的语句。

    参考答案(点开前请先独立完成)

    解题思路:从列表创建 Series 时,索引默认由 Pandas 提供整数位置索引(0、1、2……),除非显式传入 index;从字典创建 Series 时,字典的键自动成为索引。转换语句:Series 转列表用 list(s)s.tolist();DataFrame 转 NumPy 数组用 df.values

    # 验证脚本:两种数据源的索引来源与双向转换
    import pandas as pd  # 导入pandas库
    s = pd.Series([10, 20, 30], index=['a', 'b', 'c'])  # 列表作数据、显式给索引
    print(list(s), s.tolist())  # Series转列表的两种写法
    df = pd.DataFrame({'价格': [1.0, 2.0], '数量': [3, 4]}, index=['甲', '乙'])  # 建数据框
    print(type(df.values), df.values.dtype)  # DataFrame转NumPy数组并查看类型

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    [10, 20, 30] [10, 20, 30]
    <class 'numpy.ndarray'> float64

    回扣主线:“数据源决定写法”与四类结构的双向转换见第 章节 10 章”要点”第 2、3 条。

  3. 变式任务(平台任务同型改造):参照平台任务二,用 3 个交易日和 2 只基金构造一个 3×2 的净值数据框,再用 .values 取出其数组形式并打印,对照两者行列方向的对应关系。

    参考答案(点开前请先独立完成)

    解题思路:用二维嵌套结构作数据、index 给 3 个交易日、columns 给 2 只基金,得到 3×2 数据框;.values 取出的数组行对应交易日、列对应基金——DataFrame 的行方向与数组的第 1 维、列方向与第 2 维一一对应,打印后逐行对照即可验证。

    # 变式脚本:3日×2基金净值数据框与其数组形式
    import pandas as pd  # 导入pandas库
    import numpy as np  # 导入NumPy库
    nav_dates = ['2024-01-02', '2024-01-03', '2024-01-04']  # 3个交易日作行索引
    fund_names = ['华夏成长混合', '易方达平稳增长']  # 2只基金作列名
    nav_data = np.array([[1.012, 0.986], [1.018, 0.991], [1.009, 0.994]])  # 3×2净值数据
    nav_df = pd.DataFrame(data=nav_data, index=nav_dates, columns=fund_names)  # 组装数据框
    print(nav_df)  # 打印数据框
    print(nav_df.values)  # 取出数组形式,行列方向与数据框一致

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

                华夏成长混合  易方达平稳增长
    2024-01-02   1.012    0.986
    2024-01-03   1.018    0.991
    2024-01-04   1.009    0.994
    [[1.012 0.986]
     [1.018 0.991]
     [1.009 0.994]]

    注意:以上为变式代码;列表 49.1 的平台任务仍须按原始代码原样输入教学平台。

    回扣主线:从 NumPy 数组创建 DataFrame 与 index/columns 的对应关系见第 章节 10 章。

  4. 变式任务:把本章”设置DataFrame的索引”示例中的 pd.date_range('2024-01-01', periods=5) 改为按工作日频率生成(freq='B'),观察索引有何变化,并尝试用 df_indexed.loc 按日期切片取出其中 3 行。

    参考答案(点开前请先独立完成)

    解题思路freq='B' 只生成工作日(business day),跳过周六日。有一个值得注意的细节:2024-01-01 恰是周一,其后的 01-02 至 01-05 依次为周二至周五,中间没有周末,因此从 2024-01-01 出发生成 5 个工作日与逐日日历完全相同——索引”看不出变化”正是本次观察的诚实结论;把起点换到周五(如 2024-01-05),freq='B' 就会跳过 01-06、01-07 两个周末日,索引变为 01-05、01-08、01-09、01-10、01-11。loc 的日期切片两端都包含,可直接取出中间 3 行。

    # 变式脚本:工作日频率的日期索引与按标签切片
    import pandas as pd  # 导入pandas库
    import numpy as np  # 导入NumPy库
    date_monday = pd.date_range('2024-01-01', periods=5, freq='B')  # 周一起始的5个工作日
    date_friday = pd.date_range('2024-01-05', periods=5, freq='B')  # 周五起始的5个工作日
    print([d.strftime('%Y-%m-%d') for d in date_monday])  # 无周末间隔,与日历逐日相同
    print([d.strftime('%Y-%m-%d') for d in date_friday])  # 跳过周末,衔接下周一
    df_indexed = pd.DataFrame({'收盘价': np.arange(5) + 10.0}, index=date_monday)  # 以工作日为行索引
    print(df_indexed.loc['2024-01-02':'2024-01-04'])  # 按日期标签切片,两端都包含

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    ['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05']
    ['2024-01-05', '2024-01-08', '2024-01-09', '2024-01-10', '2024-01-11']
                 收盘价
    2024-01-02  11.0
    2024-01-03  12.0
    2024-01-04  13.0

    回扣主线set_index/日期索引为按标签切片打基础的论述见第 章节 10 章”设置DataFrame的索引”一节。